﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using Raven.Server;
using Raven.Server.Config.Attributes;
using Raven.Server.Config.Categories;
using Raven.Server.Extensions;

namespace TypingsGenerator
{
    public class ConfigurationExporter
    {
        private readonly Dictionary<string, Dictionary<string, string>> _configuration = new();
        
        private const string TargetFile = "configuration.ts";
        
        public void Create(string targetDir)
        {
            ScanAssembly(typeof(RavenServer).Assembly);
            
            WriteConfigurationFile(targetDir);
        }
        
        private void WriteConfigurationFile(string targetDir)
        {
            var builder = new StringBuilder();
            builder.AppendLine("// This class is autogenerated. Do NOT modify");
            builder.AppendLine("class configurationConstants {");

            foreach (var kvp in _configuration)
            {
                builder.AppendLine("    static " + LowerCaseFirst(kvp.Key) + " = {");

                var currentConfig = 0;
                var configLength = kvp.Value.Count;
                
                foreach (var configKvp in kvp.Value)
                {
                    currentConfig++;
                    var separator = configLength == currentConfig ? "" : ",";
                    builder.AppendLine("        " + configKvp.Key + ": \"" + configKvp.Value + "\"" + separator);
                }
                builder.AppendLine("    }");
            }
            
            builder.AppendLine("}");
            builder.AppendLine();
            builder.AppendLine("export = configurationConstants;");
            
            File.WriteAllText(Path.Combine(targetDir, TargetFile), builder.ToString());
        }
        
        private void ScanAssembly(Assembly assembly)
        {
            foreach (Type type in assembly.GetTypes())
            {
                if (type.IsSubclassOf(typeof(ConfigurationCategory)))
                {
                    foreach (var propertyInfo in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public))
                    {
                        foreach (var actionAttribute in propertyInfo.GetCustomAttributes<ConfigurationEntryAttribute>(false))
                        {
                            RegisterEndpoint(actionAttribute, propertyInfo);
                        }
                    }
                }
            }
        }
        
        private void RegisterEndpoint(ConfigurationEntryAttribute configurationEntry, PropertyInfo propertyInfo) 
        {
            var name = propertyInfo.DeclaringType.Name;

            var configurationIdx = name.IndexOf("Configuration");
            if (configurationIdx == -1)
            {
                throw new ArgumentException("Invalid Configuration type name: " + name);
            }
            
            var configurationKey = name.Substring(0, configurationIdx);
            
            var configurationLowerCased = LowerCaseFirst(configurationKey);

            var perFileDict = _configuration.GetOrAdd(configurationLowerCased);

            perFileDict[LowerCaseFirst(propertyInfo.Name)] = configurationEntry.Key;
        }
        
        private static string LowerCaseFirst(string input)
        {
            return input[0].ToString().ToLower(CultureInfo.InvariantCulture) + input.Substring(1);
        }
    }
}
